home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / zcpp_jae.zip / CLASS.C < prev    next >
C/C++ Source or Header  |  1990-07-06  |  19KB  |  605 lines

  1. /*
  2.  
  3.  
  4.  Copyright (C) 1990 Texas Instruments Incorporated.
  5.  
  6.  Permission is granted to any individual or institution to use, copy, modify,
  7.  and distribute this software, provided that this complete copyright and
  8.  permission notice is maintained, intact, in all copies and supporting
  9.  documentation.
  10.  
  11.  Texas Instruments Incorporated provides this software "as is" without
  12.  express or implied warranty.
  13.  
  14.  
  15.  *
  16.  * Edit history
  17.  * Created: LGO 30-Mar-89 -- Initial design and implementation.
  18.  * 
  19.  * The CLASS macro
  20.  *
  21.  * The #pragma defmacro class call will pass arbitrary parameters to the
  22.  * defmacro. We use this mechanism to tell the class macro to stick
  23.  * macro calls into the class definition.  In this way the class macro
  24.  * doesn't generate code itself, all code is defined in user modifiable
  25.  * header files.  The macros will be called with the class name and the
  26.  * base-class name.  When slots or methods are specified a BODY argument
  27.  * is provided with a macro call for each slot or method.  The slot macros
  28.  * are passed the slot type and name, and the method macros are passed the
  29.  * result type, name and arglist.  The arglist is passed in double quotes,
  30.  * because of the embedded commas (see the #~ MACRO operator).
  31.  * 
  32.  * There is a need to select from categories of things within the class:
  33.  * 
  34.  * slots methods            // Only one may be specified
  35.  * virtual inline normal friend static
  36.  *                          // if none specified, use virtual inline normal
  37.  * private protected public // if none specified, all slots or methods used
  38.  * inside outside        // where to expand the macro call
  39.  * 
  40.  * These are specfied with the classmac defmacro with
  41.  * the following grammar:
  42.  *   classmac(macro_name, :REST keywords)
  43.  * where keywords is zero or more of 
  44.  *    inside | slots | methods | virtual | inline | normal |
  45.  *    private | protected | public
  46.  *
  47.  * When a keyword has a value, the value is the name of a macro to call
  48.  * when the preceding keywords apply.  The macros will be expanded outside
  49.  * and after the class definition, unless the "inside" keyword is found
  50.  * anywhere in the parameter list. In this case the macro is expanded 
  51.  * inside the class definition, at the end.
  52.  *
  53.  * Example:
  54.  * 
  55.  * #pragma defmacro class "class"
  56.  * #pragma defmacro classmac "classmac" delimiter=)
  57.  * classmac(DECLARE_Generic, inside)
  58.  * classmac(IMPLEMENT_Generic)
  59.  * classmac(generate_map_over_slots, slots=generate_map_slot)
  60.  * classmac(my_generate_methods, methods, public=public_methods, \
  61.  *                               methods, protected, private=misc_methods)
  62.  * 
  63.  * struct method_entry {
  64.  *   Symbol* name;
  65.  *   Symbol* type;
  66.  *   char* args;
  67.  *   Boolean is_public;
  68.  * };
  69.  * 
  70.  * // Invoked from the class macro
  71.  * MACRO my_generate_methods(class_name, base_class, BODY: methods) {
  72.  *   method_entry class_name##_method_table[] = { methods }; }
  73.  * 
  74.  * // expanded within my_generate_methods
  75.  * MACRO public_methods(type, name, args) {
  76.  *   {SYM(name), SYM(type), args, TRUE} }
  77.  * 
  78.  * MACRO misc_methods(type, name, args) {
  79.  *   {SYM(name), SYM(type), args, FALSE} }
  80.  * 
  81.  * MACRO generate_map_over_slots(class_name, base_class, BODY: slots) {
  82.  *  Boolean class_name::map_over_slots(Slot_Mapper procedure, void* rock=NULL){
  83.  *   return base_class::map_over_slots(procedure rock) slots;
  84.  *  }
  85.  * }
  86.  * 
  87.  * MACRO generate_map_slot(type, name, value) {
  88.  *   || ((*procedure)(this, #name, &name, SYM(type), rock)) }
  89.  * protected:
  90.  *   virtual Symbol** type_list();
  91.  * public:
  92.  *   Long(long value) { num = value; };          // Make a long
  93.  *   long operator long() { return this->num; }; // Get the value out
  94.  *   virtual Boolean map_over_slots(Slot_Mapper procedure, void* rock = NULL);
  95.  * };
  96.  * 
  97.  * extern Symbol* Generic_types[];
  98.  * Symbol* Long_types[] = {sym(Long), (Symbol*) Generic_types, NULL};
  99.  * Symbol** Long::type_list() {return Long_types;};
  100.  * 
  101.  * Boolean Long::map_over_slots (Slot_Mapper procedure, void* rock = NULL) {
  102.  *   return (
  103.  *     Object::map_over_slots(procedure rock) // Do inherited slots first
  104.  *     || ((procedure)(this, "num", &num, SYMBOL("long"), rock));
  105.  *   )
  106.  * }
  107.  *
  108.  */
  109.  
  110. #include "defmacio.h"
  111. #include "macro.h"
  112.  
  113. static char* classkeys[] =
  114.  {"slots", "methods", "virtual", "inline", "normal", "friend", "static",
  115.   "private", "protected", "public", "inside", NULL};
  116.  
  117. #define s_slots     0x001
  118. #define s_methods   0x002
  119. #define s_virtual   0x004
  120. #define s_inline    0x008
  121. #define s_normal    0x010
  122. #define s_friend    0x020
  123. #define s_static    0x040
  124. #define s_private   0x080
  125. #define s_protected 0x100
  126. #define s_public    0x200
  127. #define s_inside    0x400
  128.  
  129. /*
  130.  * There's a CMAC for every classmac encountered.  The first one is pointed
  131.  * to by Cmac_head.  Additional classmac's are added at Cmac_tail.
  132.  * There's a CBODY for every macro in the body of a CMAC.
  133.  */
  134. typedef struct Cbody {
  135.   struct Cbody* next;              /* Next macro or NULL */
  136.   char* name;                  /* macro name */
  137.   unsigned int specifiers;          /* specifier bit set */
  138. } CBODY;
  139.  
  140. typedef struct Cmac {
  141.   struct Cmac* next;              /* Next macro or NULL */
  142.   struct Cbody* body;              /* List of body macro spec's */
  143.   char* macro;                  /* macro name */
  144.   int is_inside;              /* True when inside, else outside */
  145. } CMAC;
  146.  
  147. static CMAC* Cmac_head = NULL;          /* First class macro */
  148. static CMAC* Cmac_tail = NULL;          /* Last class macro */
  149.  
  150. #define BSIZE 512
  151. /*
  152.  * Return the mask for a class keyword, else zero
  153.  */
  154. static int class_key(arg)
  155.     char* arg;
  156. {
  157.   char** key = classkeys;
  158.   int type = 1;
  159.   while (*key != NULL) {          /* Find keyword */
  160.     if (strcmp(*key, arg) == 0) break;
  161.     key++;
  162.     type = type << 1;
  163.   }
  164.   if (*key == NULL)
  165.     return 0;
  166.   else
  167.     return type;
  168. }
  169.  
  170. /*
  171.  * Parse the arguments to the class macro
  172.  */
  173. int classmac(argc, argv)
  174.      int   argc;
  175.      char* argv[];
  176. {
  177.   Arg* args = NULL;
  178.   Arg* argp;
  179.   char macname[BSIZE];
  180.   if(copytoken(macname) == NULL)      /* get macro name */
  181.     return(1);
  182.   if((args = macro_args(macname)) == &arg_error) /* Gather arguments */
  183.     return(1);
  184.   {
  185.     int mactype = 0;
  186.     CMAC* mac = (CMAC*) getmem(sizeof(CMAC));
  187.     CBODY** next = &mac->body;
  188.     mac->body = NULL;
  189.     mac->macro = args->name;          /* First parameter is macro name */
  190.     mac->is_inside = FALSE;
  191.     mac->next = NULL;
  192.     if (Cmac_head == NULL) {          /* Link in first macro */
  193.       Cmac_head = mac;
  194.       Cmac_tail = mac;
  195.     } else {                  /* Link in next macro */
  196.       Cmac_tail->next = mac;
  197.       Cmac_tail = mac;
  198.     }
  199.     /* Loop over arguments */
  200.     for (argp = args->next; argp != NULL; argp = argp->next) {
  201.       int type = class_key(argp->name);
  202.       if (type == 0) {
  203.     fprintf(stderr, "%s(%s ...) Unknown keyword %s\n",
  204.         macname, mac->macro, argp->name);
  205.     return(1);
  206.       }
  207.       if (type == s_inside)          /* Check for inside */
  208.     mac->is_inside = TRUE;
  209.       else
  210.     mactype |= type;
  211.       if (*argp->value) {      /* When '=' found, define macro */
  212.     if ((mactype & (s_virtual | s_inline | s_normal)) == 0)
  213.       mactype |= (s_virtual | s_inline | s_normal);
  214.     if ((mactype & (s_private | s_protected | s_public)) == 0)
  215.       mactype |= (s_private | s_protected | s_public);
  216.     { CBODY* body = (CBODY*) getmem(sizeof(CBODY));    
  217.       body->name = argp->value;
  218.       body->specifiers = mactype;
  219.       body->next = NULL;
  220.       *next = body;
  221.       next = &body->next;
  222.       mactype = 0;
  223.     }
  224.       }
  225.     }
  226.     if (mactype != 0) {
  227.       fprintf(stderr, "%s(%s ...) macro to call not specified\n",
  228.           macname, mac->macro, argp->name);
  229.       return(1);
  230.     }
  231.   }
  232.   return 0;
  233. }
  234.  
  235. /*
  236.  * Instead of malloc'ing for every token, all tokens are kept in
  237.  * work_string, seperated by '\0's. Since work_string may grow, 
  238.  * we don't keep pointers into it, but use offsets instead.
  239.  *
  240.  * This macro converts a work-string offset into a char*
  241.  */
  242. #define WSTRING(n) (work_string->buff+(n))
  243.  
  244. /*
  245.  * There's a Cmember struct for each member (slot method or friend)
  246.  * of a class.  There's a vector of members kept on the stack.
  247.  */
  248. typedef struct Cmember {
  249.   unsigned int specifiers;          /* specifier bit set */
  250.   unsigned int type;              /* member type index */
  251.   unsigned int name;              /* macro name index */
  252.   unsigned int value;              /* Value index if slot */
  253.   unsigned int parms;              /* Parameter index if function */
  254. } CMEMBER;
  255.  
  256. /*
  257.  * Read in a member (data or function)
  258.  * Fills in tokens with member type, name, parameters/value
  259.  * Fills in delimiters with the delimiter BEFORE the associated token.
  260.  */
  261. static int class_read(tokens, delimiters, blanks)
  262.      int tokens[20];
  263.      char  delimiters[20];
  264.      STRING blanks;
  265. {   
  266.   int ntoken = 0;
  267.   char* token;
  268.   char delim = ' ';
  269.   delimiters[0] = ' ';
  270.  
  271.   for(;;) {                  /* Loop over tokens in member */
  272.     char c;
  273.     char next_delim;
  274.     blanks->buffp = blanks->buff;
  275.     c = append_blanks(blanks);
  276.     if (c == EOF) break;          /* unexpected end of file */
  277.     puts(blanks->buff);
  278.     /* Get next token */
  279.     next_delim = EOS;
  280.     switch (c) {
  281.     case ',':
  282.       if (ntoken > 0) {
  283.     unget();               /* exit and leave comma for next time */
  284.     return ntoken;
  285.       }
  286.       break;
  287.     case ';':
  288.       putchar(c);
  289.       if (ntoken==0) {
  290.     continue;          /* }; - ignore ; */
  291.       }
  292.       /* fall through */
  293.     case EOF:
  294.     case '}':
  295.       return ntoken;              /* normal return */
  296.     case ':':
  297.       putchar(c);
  298.       delim = c;
  299.       c = append_blanks(blanks);
  300.       puts(blanks->buff);
  301.       break;
  302.     case '<':                  /* operator<< or parmtype */
  303.       if(!strncmp("operator", WSTRING(tokens[ntoken-1]), 8)) {
  304.     putchar(c);
  305.     work_string->buffp--;
  306.     append_char(work_string, c);
  307.     append_char(work_string, EOS);
  308.     continue;
  309.       } /* else fall through */
  310.     case '[':                  /* operator[] */
  311.       unget();
  312.       work_string->buffp--;
  313.       delim = (c=='[') ? ']' : '>';
  314.       token = scan_list(delim);      /* Append to prevous token */
  315.       puts(token);
  316.       continue;
  317.     case '#':                  /* #if or something */
  318.       do putchar(c)              /* Just copy it */
  319.       while ((c = getchar()) != '\n' && c != EOF);
  320.       putchar(c);
  321.       continue;
  322.     case '(': delim = c; next_delim = ')'; break;
  323.     case '{': delim = c; next_delim = '}'; break;
  324.     case '_':                  /* Beginning of identifier */
  325.     case '$': next_delim = ' '; break;      /* Beginning of identifier */
  326.     }
  327.  
  328.     if (isalnum(c) || next_delim != EOS) {
  329.       unget();
  330.       if (next_delim == EOS) next_delim = ' ';
  331.       token = scan_list(next_delim);      /* Get next token */
  332.       puts(token);
  333.       tokens[ntoken] = token - work_string->buff;
  334.       delimiters[ntoken++] = delim;
  335.     } else if (ntoken == 0 && c != '~' && c != ',') {
  336.       fprintf(stderr,"class: Strange character '%c' at beginning of member\n"
  337.           , c);
  338.       return 0;
  339.     } else if (c == ':' && delimiters[ntoken-1] == ':') {
  340.       putchar(c);
  341.       work_string->buffp--;          /* :: found, append next token */
  342.       append_STRING(work_string, "::");
  343.       token = scan_list(' ');
  344.       puts(token);
  345.     } else {
  346.       putchar(c);
  347.       tokens[ntoken] = work_string->buffp - work_string->buff;
  348.       delimiters[ntoken++] = c;
  349.       append_char(work_string, EOS);
  350.       next_delim = ' ';
  351.     }
  352.     delim = next_delim;
  353.     if (delim == '}')
  354.       return ntoken;              /* End of inline */
  355.   } /* end token loop */
  356.   /* unexpected end of file */
  357.   return ntoken;
  358. }
  359.  
  360. unsigned int print_flags(n)          /* ***** DEBUG FUNCTION ***** */
  361.      unsigned int n;
  362. {
  363.   int i;
  364.   for (i=0; i<16; i++)
  365.     if ((1<<i) & n)
  366.       fprintf(stderr, "%s ", classkeys[i]);
  367.   fprintf(stderr, "\n");
  368.   return n;
  369. }
  370.  
  371.  
  372. /* Copy body of class */
  373. static int class_body(members, max_members)
  374.     CMEMBER members[];
  375.     int max_members;
  376. {
  377.   STRING blanks = make_STRING(80);
  378.   int tokens[20];
  379.   char  delimiters[20];
  380.   int protection = s_public;
  381.   int i;
  382.   int nmember;
  383.  
  384.   scan_start();
  385.   *work_string->buffp++ = EOS;          /* First char for null string */
  386.  
  387.   for(nmember=0; nmember< max_members; nmember++) { /* Loop over members */
  388.     CMEMBER* m = &members[nmember];
  389.     int start;
  390.     int end;
  391.     int ntoken;
  392.     int member_type = 0;
  393.     m->type = 0;
  394.     m->name = 0;
  395.     m->value = 0;
  396.     m->parms = 0;
  397.     /*
  398.      * Read a member
  399.      */
  400.     ntoken = class_read(tokens, delimiters, blanks);
  401.     if (ntoken == 0) goto last_member;
  402.     if (delimiters[0] == ',') {      /* comma means use same type as previous */
  403.       CMEMBER* pm = &members[nmember-1];
  404.       m->type = pm->type;
  405.       m->specifiers = pm->specifiers;
  406.       start = 0;
  407.     } else {
  408.       /*
  409.        * Get keywords at beginning of line
  410.        */
  411.       for (start=0; start<ntoken; start++) { /* Loop over tokens */
  412.     int mask = class_key(WSTRING(tokens[start]));
  413.     if (mask & (s_public | s_private | s_protected)) {
  414.       protection = mask;
  415.     }
  416.     else if (mask & (s_virtual | s_inline | s_normal | s_friend | s_static)){
  417.       member_type |= mask;
  418.     }
  419.     else break;
  420.       }                      /* Type is normal if isn't unusual */
  421.       if ((member_type&(s_virtual|s_inline|s_normal|s_friend|s_static)) == 0)
  422.     member_type |= s_normal;
  423.     }
  424.     /*
  425.      * What's left is the result-type, name, arglist, body and semicolon.
  426.      * Because we don't know what a type looks like, search BACKWARD
  427.      * for these items.  What's left is the result type.
  428.      */
  429.     for (end = ntoken-1; end >= start; end--) {
  430.       switch (delimiters[end]) {
  431.       case '{':                  /* Body */
  432.     if (member_type & s_methods)
  433.       member_type |= s_inline;      /* if a method must be inline */
  434.     ntoken--;              /* Don't save body */
  435.     break;
  436.       case ')':                  /* Parameter list */
  437.     if (end > start)          /* If token after parm list */
  438.       *(WSTRING(tokens[end-1])-1) = ' '; /* concatenate parm list */
  439.     break;
  440.       case '(':                  /* Parameter list */
  441.     m->parms = tokens[end];
  442.     m->name = tokens[--end];      /* Token before parm list is name */
  443.     member_type |= s_methods;      /* Must be a method */
  444.     goto exit;              /* Everything else is type */
  445.       case '>':
  446.       case ' ':                  /* slot Name */
  447.     m->name = tokens[end];          /* Token before parm list is name */
  448.     member_type |= s_slots;          /* Must be a slot */
  449.     goto exit;              /* Everything else is type */
  450.       case '=':
  451.     m->name = tokens[end];
  452.     m->value = tokens[end+1];
  453.     for (i=end+1; i<ntoken; i++)
  454.       *(WSTRING(tokens[i])-1) = ' '; /* concatenate values */
  455.     member_type |= s_slots;
  456.     goto exit;
  457.       case ':':                  /* field width (ignored) */
  458.     break;
  459.       default:                  /* debug */
  460.     fprintf(stderr, "class: Strange character \'%c\' found after ",
  461.         delimiters[end]);
  462.     for (i=0; i<end; i++)
  463.       fprintf(stderr, "%c%s",
  464.           delimiters[i], (tokens[i]<0) ? "" : WSTRING(tokens[i]));
  465.     fprintf(stderr, "\n");
  466.       }
  467.     }
  468.     fprintf(stderr, "class: Couldn't find member after "); /* DEBUG */
  469.     fprintf(stderr, "%s%c",
  470.         (tokens[i]<0) ? "" : WSTRING(tokens[i]), delimiters[i]);
  471.     fprintf(stderr, "\n");
  472.     continue;
  473.   exit:
  474.     if (m->type != NULL) continue;      /* if comma before member */
  475.     m->type = tokens[start];
  476.     for (i=start+1; i<end; i++) {
  477.       *(WSTRING(tokens[i])-1) = ' '; /* concatenate types */
  478.       if ((*WSTRING(tokens[i])) == EOS)
  479.     *(WSTRING(tokens[i])-1) = delimiters[i];
  480.     }
  481.     m->specifiers = member_type | protection;
  482.   } /* end for token */
  483.  last_member:
  484.   destroy_STRING(blanks);
  485.   if (nmember >= max_members)
  486.     cerror("More than 200 members in a class", "");
  487.   return nmember;
  488. }
  489.  
  490. void class_macros(class, base, is_inside, members, nmember)
  491.     char* class;
  492.     char* base;
  493.     int is_inside;
  494.     CMEMBER members[];
  495.     int nmember;
  496. {
  497.   CMAC* mac;
  498.   char line[200];
  499.   for (mac=Cmac_head; mac != NULL; mac = mac->next) { /* for each macro */
  500.     int n;
  501.     if (mac->is_inside == is_inside) {
  502.       sprintf(line, "\n%s(%s, %s)", mac->macro, class, base); puts(line);
  503.       if (mac->body != NULL) {
  504.     puts(" {\n");
  505.     for(n=0; n<nmember; n++) {          /* for each member */
  506.       CMEMBER* m = &members[n];
  507.       CBODY* body;
  508.       unsigned int mspec = m->specifiers;
  509.       for (body=mac->body; body!=NULL; body=body->next) { /* for bodies */
  510.         unsigned int bspec = body->specifiers;
  511.         if ((mspec&bspec&(s_slots|s_methods)) &&
  512.         (mspec&bspec&(s_private|s_protected|s_public)) &&
  513.         (mspec&bspec&(s_virtual|s_inline|s_normal|s_friend|s_static))){
  514.           sprintf(line, "%s(%s, %s, %s)\n",
  515.               body->name, WSTRING(m->type), WSTRING(m->name),
  516.               WSTRING((mspec & s_slots) ? m->value : m->parms));
  517.           puts(line);
  518.         }
  519.       }
  520.     }                  /* for members in class */
  521.     putchar('}');
  522.       }
  523.       putchar('\n');
  524.     }
  525.   } /* for all macros */
  526. }
  527.  
  528. int class_macro(argc, argv)
  529.      int argc;
  530.      char* argv[];
  531. {
  532.   char c;
  533.   char buff[BSIZE];
  534.   char* classname;
  535.   STRING basename = make_STRING(80);
  536.   if(copytoken(buff) == NULL)      /* Skip class keyword */
  537.     return(1);
  538.   puts(buff);
  539.   classname = savestring(scan_next(' ')); /* Get class name */
  540.   putchar(' ');
  541.   puts(classname);
  542.   putchar(' ');
  543.   c = skip_blanks();
  544.   putchar(c);
  545.   switch (c) {
  546.   case '*':              /* Type (e.g. extern class foo* bar;) */
  547.   case ';':              /* Forward reference */
  548.     { char* buffp = buff;
  549.       char* namep = classname;
  550.       while((c = *namep++) != EOS &&
  551.         (isalpha(c) || c == '_' || c == '$'))
  552.     *buffp++ = c;
  553.       if (c == '<') {          /* A template, ensure parmtype is defined */
  554.     extern int parmtype();
  555.     *buffp = EOS;
  556.     new_defmacro(savestring(buff), FALSE, FALSE, '>', '<',
  557.              parmtype, "parmtype", NULL);
  558.       }
  559.     }
  560.     free(classname);
  561.     classname = NULL;
  562.     while ((c = getchar()) != EOF) putchar(c); /* copy rest of input */
  563.     break;
  564.   case '{':                  /* No base class */
  565.     break;
  566.   case ':':
  567.     { char* base;
  568.     next_base:
  569.       base = scan_next(' ');          /* Copy the base class name */
  570.       puts(base);
  571.       if(!strcmp(base, "public") ||      /* If first word was public, */
  572.      !strcmp(base, "private") ||      /* or private */
  573.      !strcmp(base, "virtual")) {      /* or virtual */
  574.     putchar(' ');
  575.     goto next_base;              /* Ignore and get base class name */
  576.       }
  577.       append_STRING(basename, base);
  578.       c = skip_blanks();
  579.       if(c == ',') {
  580.     putchar(c);
  581.     append_char(basename, c);
  582.     goto next_base;
  583.       }
  584.       if(c != '{') {
  585.     fprintf(stderr, "class: Syntax error, '%c' instead of { after base class\n", c);
  586.     return 1;
  587.       }
  588.     }
  589.     putchar(c);
  590.     break;
  591.   default: 
  592.     fprintf(stderr, "class: Syntax error, class %s %c\n", classname, c);    
  593.     return 1;
  594.   }
  595.   if (classname != NULL) {
  596.     CMEMBER members[200];
  597.     int nmember = class_body(members, 200);
  598.     class_macros(classname, basename->buff, TRUE, members, nmember);/*Inside*/
  599.     puts("};\n"); /* terminate class definition */
  600.     class_macros(classname, basename->buff, FALSE, members,nmember);/*Outside*/
  601.   }
  602.   destroy_STRING(basename);
  603.   return 0;
  604. }
  605.